package com.tangding.aspect;

import java.util.Date;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.google.common.base.Strings;
import com.tangding.common.exception.GException;
import com.tangding.common.holder.SpringContextHolder;
import com.tangding.common.logx.LogHelp;
import com.tangding.common.utils.JacksonUtil;
import com.tangding.hprose.api.server.command.Response;
import com.tangding.log.LogMatcher;
import com.tangding.log.impl.DefaultLogMatcher;
import com.tangding.service.dao.log.LogRepository;
import com.tangding.service.dao.user.UserRepository;
import com.tangding.service.entity.user.User;

/**
 * @description 
 *
 * @author Ranger
 * @version 2017年12月7日_上午9:32:22
 *
 */
@Component
@Aspect
public class LogAspect {
	
	static LogRepository logRepository;
	
	@Autowired
	public void setLogRepository(LogRepository logRepository) {
		LogAspect.logRepository = logRepository;
	}
	
	@Autowired
	UserRepository userRepository;
	

	static ExecutorService pool = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors() * 2);
	
	@SuppressWarnings("unchecked")
	@AfterReturning(value = "execution(public * com.tangding.hprose.api.server.command..*.exe(..))", returning = "response")
	public void afterReturnLog(JoinPoint joinPoint, Response response) {
		Object target = joinPoint.getTarget();
		String cmd = target.getClass().getSimpleName();
		User user = null;
		Object[] args = joinPoint.getArgs();
		if ("HproseRedirect".equalsIgnoreCase(cmd)) {//转发请求
			Object[] as = ((Object[])args[0]);
			cmd = (String) as[0];
			Object[] newArgs = new Object[as.length - 1];
			System.arraycopy(as, 1, newArgs, 0, newArgs.length);
			args[0] = newArgs;
			user = findUser(args);
		}else {
			if ("Login".equals(cmd)) {
				Map<String, Object> map = (Map<String, Object>) ((Object[])args[0])[0];
				String uid = (String) map.get("account");
				user = userRepository.getUserByUid(uid);
				if (user == null) {
					buildUnknownUser(uid);
				}
			}else {
				user = findUser(args);
			}
		}
		insertLog(user, cmd + "Log", args, response);
	}
	
	@SuppressWarnings("unchecked")
	@AfterThrowing(throwing="e", pointcut = "execution(public * com.tangding.hprose.api.server.command..*.exe(..))")
	public void afterGException(JoinPoint joinPoint, GException e) {
		Object target = joinPoint.getTarget();
		String cmd = target.getClass().getSimpleName();
		User user = null;
		Object[] args = joinPoint.getArgs();
		if ("HproseRedirect".equalsIgnoreCase(cmd)) {//转发请求
			Object[] as = ((Object[])args[0]);
			cmd = (String) as[0];
			Object[] newArgs = new Object[as.length - 1];
			System.arraycopy(as, 1, newArgs, 0, newArgs.length);
			args[0] = newArgs;
			user = findUser(args);
		}else {
			if ("Login".equals(cmd)) {
				Map<String, Object> map = (Map<String, Object>) ((Object[])args[0])[0];
				String uid = (String) map.get("account");
				user = userRepository.getUserByUid(uid);
				if (user == null) {
					buildUnknownUser(uid);
				}
			}else {
				user = findUser(args);
			}
		}
		insertLog(user, cmd + "Log", args, e);
	}
	
	@SuppressWarnings("unchecked")
	private User findUser(Object[] args) {
		if (args == null) {
			return buildUnknownUser(null);
		}else {
			Object[] as = (Object[])args[0];
			Map<String, Object> map = (Map<String, Object>) as[as.length - 1];
			String uid = (String) map.get("uid");
			User user = userRepository.getUserByUid(uid);
			if (user == null) {
				return buildUnknownUser(uid);
			}
			return user;
		}
	}
	
	private User buildUnknownUser(String uid) {
		User user = new User();
		if (!Strings.isNullOrEmpty(uid)) {
			user.setUid(uid);
		}
		user.setName("未知用户");
		return user;
	}
	
	public <T> void insertLog(User admin, String cmd, Object[] args, T t) {
		pool.submit(new Runnable() {
			public void run() {
				try {
					if (!checkNeedLog(admin, cmd)) {//不需要记录日志
						return;
					}
				} catch (GException e) {
					LogHelp.doLogAppErr(e);
					return;
				}
				LogMatcher logMatcher = findLogMatcher(cmd);
				Document doc = buildLogDoc(admin, cmd, args, logMatcher, LogType.RESPONSE);
				if (t instanceof Response) {
					doc.put("response", Document.parse(JacksonUtil.toJson(t)));
					doc.put("responseDisplay", logMatcher.responseDisplay((Response)t));
				}else if (t instanceof GException) {
					doc.put("response", logMatcher.errDisplay((GException) t));
				}
				System.out.println("---------------------");
				System.out.println(JacksonUtil.toJson(doc));
				System.out.println("---------------------");
				
				logRepository.pushLog(doc);
			}
		});
	}
	
	/*public void insertFlowLog(User admin, String cmd, Object[] args, Response response) {
		if (!checkNeedLog(cmd)) {//不需要记录日志
			return;
		}
		pool.submit(new Runnable() {
			public void run() {
				LogMatcher logMatcher = findLogMatcher(cmd);
				Document doc = buildLogDoc(admin, cmd, args, logMatcher, LogType.RESPONSE);
				doc.put("response", Document.parse(JacksonUtil.toJson(response)));
				doc.put("responseDisplay", logMatcher.responseDisplay(response));
				System.out.println("---------------------");
				System.out.println(JacksonUtil.toJson(doc));
				System.out.println("---------------------");
				
				logRepository.pushLog(doc);
			}
		});
	}
	
	public void insertFlowErrLog(User admin, String cmd, Object[] args, GException exception) {
		if (!checkNeedLog(cmd)) {//不需要记录日志
			return;
		}
		pool.submit(new Runnable() {
			public void run() {
				LogMatcher logMatcher = findLogMatcher(cmd);
				Document doc = buildLogDoc(admin, cmd, args, logMatcher, LogType.GEXCEPTION);
				doc.put("response", logMatcher.errDisplay(exception));
				doc.put("responseDisplay", "操作失败");
				System.out.println("++++++++++++++++++++++");
				System.out.println(JacksonUtil.toJson(doc));
				System.out.println("++++++++++++++++++++++");
				
				logRepository.pushLog(doc);
			}
		});
	}*/
	
	private boolean checkNeedLog(User admin, String cmd) throws GException {
		if (!LogMatcher.containsCmd(cmd) && 
				SpringContextHolder.getBean(cmd) == null && 
				SpringContextHolder.getBean(cmd.substring(0, 1).toLowerCase() + cmd.substring(1)) == null) {//不需要记录日志
			return false;
		}
		
		if (Strings.isNullOrEmpty(admin.getUid()) || userRepository.findUsersByUid(admin.getUid()) == null) {
			return false;
		}
		return true;
	}
	
	private static LogMatcher findLogMatcher(String cmd) {
		LogMatcher logMatcher = SpringContextHolder.getBean(cmd);
		if (logMatcher == null) {
			logMatcher = SpringContextHolder.getBean(cmd.substring(0, 1).toLowerCase() + cmd.substring(1));
		}
		if (logMatcher == null) {
			logMatcher = SpringContextHolder.getBean(DefaultLogMatcher.class);
		}
		return logMatcher;
	}
	
	public static Document buildLogDoc(User admin, String cmd, Object[] args, LogMatcher logMatcher, LogType logType) {
		Document doc = new Document();
		String uid = admin.getUid();
		if (Strings.isNullOrEmpty(uid)) {
			uid = "0000";
		}
		String userName = admin.getName();
		doc.put("uid", uid);
		doc.put("name", userName);
		doc.put("time", new Date());
		doc.put("cmd", cmd);
		doc.put("cmdDisplay", logMatcher.cmdDisplay(cmd, args));
		doc.put("logType", logType.get());
		return doc;
	}
	
	public enum LogType{
		RESPONSE(0), GEXCEPTION(1);
		public int type;
		
		private LogType(int type) {
			this.type = type;
		}

		public int get() {
			return type;
		}
		
		public static LogType get(int type) {
			LogType[] types = LogType.values();
			for (LogType t : types) {
				if (t.get() == type) {
					return t;
				}
			}
			return null;
		}
	}
}
